﻿using System.Security.Cryptography;
using System.Text;
using System.IO;
using System;

namespace AES
{
    class EncryptionWithStreams
    {
        private const int RIJNDAEL_SECRET_KEY_SIZE_BITS = 256;
        private const int RIJNDAEL_INITIAL_VECTOR_SIZE_BITS = 128;
        
        private static ICryptoTransform CreateRijndaelEncryptor(string password)
        {
            byte[] salt = Encoding.ASCII.GetBytes(password.Length.ToString());
            PasswordDeriveBytes passwordDerivedBytes = new PasswordDeriveBytes(password, salt);
            byte[] secretKey = passwordDerivedBytes.GetBytes(RIJNDAEL_SECRET_KEY_SIZE_BITS / 8);
            byte[] initialVector = passwordDerivedBytes.GetBytes(RIJNDAEL_INITIAL_VECTOR_SIZE_BITS / 8);
            RijndaelManaged RijndaelCipher = new RijndaelManaged();
            ICryptoTransform encryptor = RijndaelCipher.CreateEncryptor(secretKey, initialVector);
            return encryptor;
        }

        public static byte[] EncryptBytes(byte[] bytesToEncrypt, string password)
        {
            ICryptoTransform encryptor = CreateRijndaelEncryptor(password);
            MemoryStream memoryStream = new MemoryStream();
            CryptoStream cryptoStream =
                new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
            using (cryptoStream)
            {
                cryptoStream.Write(bytesToEncrypt, 0, bytesToEncrypt.Length);
                cryptoStream.FlushFinalBlock();
                byte[] encryptedBytes = memoryStream.ToArray();
                return encryptedBytes;
            }
        }

        private static string EncryptString(string stringToEncrypt, string password)
        {
            byte[] bytesToEncrypt = Encoding.UTF8.GetBytes(stringToEncrypt);
            byte[] encryptedBytes = EncryptBytes(bytesToEncrypt, password);
            string encryptedString = Convert.ToBase64String(encryptedBytes);
            return encryptedString;
        }

        private static ICryptoTransform CreateRijndaelDecryptor(string password)
        {
            byte[] salt = Encoding.ASCII.GetBytes(password.Length.ToString());
            PasswordDeriveBytes passwordDerivedBytes = new PasswordDeriveBytes(password, salt);
            byte[] secretKey = passwordDerivedBytes.GetBytes(RIJNDAEL_SECRET_KEY_SIZE_BITS / 8);
            byte[] initialVector = passwordDerivedBytes.GetBytes(RIJNDAEL_INITIAL_VECTOR_SIZE_BITS / 8);
            RijndaelManaged RijndaelCipher = new RijndaelManaged();
            ICryptoTransform decryptor = RijndaelCipher.CreateDecryptor(secretKey, initialVector);
            return decryptor;
        }

        private static byte[] DecryptBytes(byte[] encryptedBytes, string password)
        {
            ICryptoTransform encryptor = CreateRijndaelDecryptor(password);
            MemoryStream encryptedBytesStream = new MemoryStream(encryptedBytes);
            MemoryStream decryptedBytesStream = new MemoryStream();
            CryptoStream cryptoStream =
                new CryptoStream(encryptedBytesStream, encryptor, CryptoStreamMode.Read);
            using (cryptoStream)
            {
                byte[] buffer = new byte[encryptedBytes.Length];
                while (true)
                {
                    int bytesRead = cryptoStream.Read(buffer, 0, buffer.Length);
                    if (bytesRead == 0)
                    {
                        break;
                    }
                    decryptedBytesStream.Write(buffer, 0, bytesRead);
                }
                byte[] decryptedBytes = decryptedBytesStream.ToArray();
                return decryptedBytes;
            }
        }

        private static string DecryptString(string encryptedString, string password)
        {
            byte[] encryptedBytes = Convert.FromBase64String(encryptedString);
            byte[] decryptedBytes = DecryptBytes(encryptedBytes, password);
            string decryptedString = Encoding.UTF8.GetString(decryptedBytes);
            return decryptedString;
        }
    }
}
